#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>

#include "DemoFile.h"

using namespace std;

long goThrougDir(const string& dirPath) {
    if (0 == dirPath.length()) {
        cout<<"goThrougDir, empty arg dirPath"<<endl;
        return 0;
    }
    
    // 打开目录操作
    DIR* dirp = opendir(dirPath.c_str());
    if (!dirp) {
        cout<<"goThrougDir, fail to open dirPath, "<<dirPath<<endl;
        return 0;
    }
    
// typedef struct {
//     int    __dd_fd;    /* file descriptor associated with directory */
//     long    __dd_loc;    /* offset in current buffer */
//     long    __dd_size;    /* amount of data returned */
//     char    *__dd_buf;    /* data buffer */
//     int    __dd_len;    /* size of data buffer */
//     long    __dd_seek;    /* magic cookie returned */
//     __unused long    __padding; /* (__dd_rewind space left for bincompat) */
//     int    __dd_flags;    /* flags for readdir */
//     __darwin_pthread_mutex_t __dd_lock; /* for thread locking */
//     struct _telldir *__dd_td; /* telldir position recording */
// } DIR;
    
    
    struct dirent * entry = NULL;
//#define __DARWIN_STRUCT_DIRENTRY { \
//    __uint64_t  d_ino;      /* file number of entry */ \
//    __uint64_t  d_seekoff;  /* seek offset (optional, used by servers) */ \
//    __uint16_t  d_reclen;   /* length of this record */ \
//    __uint16_t  d_namlen;   /* length of string in d_name */ \
//    __uint8_t   d_type;     /* file type, see below */ \
//    char      d_name[__DARWIN_MAXPATHLEN]; /* entry name (up to MAXPATHLEN bytes) */ \
//}
//
//struct dirent __DARWIN_STRUCT_DIRENTRY;

    
    //3,读取目录操作
    struct stat fileInfo;
    long totalFileSize = 0;
    while ((entry = readdir(dirp)) != NULL) {

       
        if (0 == strcmp(entry->d_name, ".") || 0 == strcmp(entry->d_name, "..")) {
            // . 和 .. 跳过，不然死循环
            continue;
        }
        
        string path = dirPath + "/" + string(entry->d_name);
        
        if (entry->d_type == DT_DIR) {
            cout<<"goThrougDir, type dir, "<<path<<endl;
            totalFileSize += goThrougDir(path);
        } else {
            stat(path.c_str(), &fileInfo);
//struct stat {
//    dev_t           st_dev;         /* [XSI] ID of device containing file */
//    ino_t           st_ino;         /* [XSI] File serial number */
//    mode_t          st_mode;        /* [XSI] Mode of file (see below) */
//    nlink_t         st_nlink;       /* [XSI] Number of hard links */
//    uid_t           st_uid;         /* [XSI] User ID of the file */
//    gid_t           st_gid;         /* [XSI] Group ID of the file */
//    dev_t           st_rdev;        /* [XSI] Device ID */
//#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
//    struct  timespec st_atimespec;  /* time of last access */
//    struct  timespec st_mtimespec;  /* time of last data modification */
//    struct  timespec st_ctimespec;  /* time of last status change */
//#else
//    time_t          st_atime;       /* [XSI] Time of last access */
//    long            st_atimensec;   /* nsec of last access */
//    time_t          st_mtime;       /* [XSI] Last data modification time */
//    long            st_mtimensec;   /* last data modification nsec */
//    time_t          st_ctime;       /* [XSI] Time of last status change */
//    long            st_ctimensec;   /* nsec of last status change */
//#endif
//    off_t           st_size;        /* [XSI] file size, in bytes */
//    blkcnt_t        st_blocks;      /* [XSI] blocks allocated for file */
//    blksize_t       st_blksize;     /* [XSI] optimal blocksize for I/O */
//    __uint32_t      st_flags;       /* user defined flags for file */
//    __uint32_t      st_gen;         /* file generation number */
//    __int32_t       st_lspare;      /* RESERVED: DO NOT USE! */
//    __int64_t       st_qspare[2];   /* RESERVED: DO NOT USE! */
//};
            totalFileSize += fileInfo.st_size;
            cout<<"goThrougDir, size:"<<fileInfo.st_size<<", "<<path<<endl;
        }
    }
    closedir(dirp);
    
    return totalFileSize;
}


void goThroughDirDemo(void) {
    cout<<endl<<endl<<"goThroughDirDemo"<<endl;
    
    long totalFileSize = goThrougDir("/Users/jieningzhang/dev2022/ggDemo/cpp");
    cout<<"totalFileSize : "<<totalFileSize<<endl;
}

void txtFileWrite(void) {
    cout<<endl<<endl<<"txtFileWrite"<<endl;
    
    // 获取到可执行程序的绝对路径
    char cmdPath[1024] = {0};
    getcwd(cmdPath, 1024);
    
    string filePath = string(cmdPath) + string("/Test.txt");
    
    // http://c.biancheng.net/view/7591.html

    fstream writeFile(filePath, ios::out); //以文本模式打开Test.txt 准备写入
    if (!writeFile) {
        cout<<"fail to open file to write : "<<filePath<<endl;
        return;
    }
    
    string poem = "水调歌头·明月几时有 \n\
苏轼 \n\
丙辰中秋，欢饮达旦，大醉，作此篇，兼怀子由。\n\
\n\
明月几时有？把酒问青天。\n\
不知天上宫阙，今夕是何年。\n\
我欲乘风归去，又恐琼楼玉宇，高处不胜寒。\n\
起舞弄清影，何似在人间。\n\
\n\
转朱阁，低绮户，照无眠。\n\
不应有恨，何事长向别时圆？\n\
人有悲欢离合，月有阴晴圆缺，此事古难全。\n\
但愿人长久，千里共婵娟。";
    
    writeFile<<poem.length()<<endl;
    cout<<"after write poem.length, tellp "<<writeFile.tellp()<<endl;
    writeFile<<poem;
    cout<<"after write poem, tellp "<<writeFile.tellp()<<endl;
    
    writeFile.close();
    
    cout<<"succ to open file to write : "<<filePath<<endl;
}

void txtFileRead(void) {
    cout<<endl<<endl<<"txtFileRead"<<endl;
    
    // 获取到可执行程序的绝对路径
    char cmdPath[1024] = {0};
    getcwd(cmdPath, 1024);
    
    string filePath = string(cmdPath) + string("/Test.txt");
    
    // http://c.biancheng.net/view/7591.html
    
    fstream readFile(filePath, ios::in); //以文本模式打开Test.txt 准备读取
    if (!readFile) {
        cout<<"fail to open file to read : "<<filePath<<endl;
        return;
    }
    
    int poemSize = 0;
    readFile>>poemSize;
    cout<<"poemSize:"<<poemSize<<", tellp "<<readFile.tellp()<<endl;
    
    string poemLine;
    while(! readFile.eof()) {
        readFile>>poemLine;
        cout<<"poemLine:"<<poemLine<<", tellp "<<readFile.tellp()<<endl;
    }
    
    readFile.close();
}

void binFileWritePerson(fstream& writeFile, PersonData& pObj) {
    
    // name长度 + name字符串内容写入 二进制文件
    //writeFile<<(int)(pObj.name.length());// 整数不能使用流写入二进制文件，因为会被当做字符流写入，占据的比特数不是int类型的长度，造成读取错乱，比如3会占用1bye, 300会占用3byte
    int strLen = (int)(pObj.name.length());
    writeFile.write((char*)(&strLen), sizeof(int));
    writeFile<<pObj.name;
    
    // addr长度 + addr字符串内容写入 二进制文件
    //writeFile<<(int)(pObj.addr.length());// 整数不能使用流写入二进制文件，因为会被当做字符流写入，占据的比特数不是int类型的长度，造成读取错乱，比如3会占用1bye, 300会占用3byte
    strLen = (int)(pObj.addr.length());
    writeFile.write((char*)(&strLen), sizeof(int));
    writeFile<<pObj.addr;
    
    // age写入 二进制文件
    short ageValue = pObj.age;
    writeFile.write((char*)(&ageValue), sizeof(short));
    // writeFile<<pObj.age; // short 不能使用流写入二进制文件
    
    // gender写入 二进制文件 gender 是 char 只会占用一个字节
    writeFile<<pObj.gender;
}

void binFileWrite(void) {
    cout<<endl<<endl<<"binFileWrite"<<endl;
    
    PersonData p1("1LiLei",      'M', 12, "505_class-1-1");
    PersonData p2("2HanMeiMei",  'F', 13, "504_class-1-2");
    PersonData p3("3JimGreen",   'M', 11, "503_class-1-3");
    
    // 获取到可执行程序的绝对路径
    char cmdPath[1024] = {0};
    getcwd(cmdPath, 1024);
    
    string filePath = string(cmdPath) + string("/Test.bin");
    fstream writeFile(filePath, ios::binary|ios::out); //以二进制文件模式打开Test.bin 准备写入
    if (!writeFile) {
        cout<<"fail to open file to write : "<<filePath<<endl;
        return;
    }
    
    // 在文件开头写入数量
    //writeFile<<(int)(300); // 整数不能使用流写入二进制文件，因为会被当做字符流写入，占据的比特数不是int类型的长度，造成读取错乱，比如3会占用1bye, 300会占用3byte
    int pnum = 3;
    writeFile.write((char*)(&pnum), sizeof(int));
    cout<<"after write num, tellp "<<writeFile.tellp()<<endl;
    binFileWritePerson(writeFile, p1);
    cout<<"after write PersonData, name="<<p1.name<<", addr="<<p1.addr<<", age="<<p1.age<<", gentder="<<p1.gender<<", tellp "<<writeFile.tellp()<<endl;
    binFileWritePerson(writeFile, p2);
    cout<<"after write PersonData, name="<<p2.name<<", addr="<<p2.addr<<", age="<<p2.age<<", gentder="<<p2.gender<<", tellp "<<writeFile.tellp()<<endl;
    binFileWritePerson(writeFile, p3);
    cout<<"after write PersonData, name="<<p3.name<<", addr="<<p3.addr<<", age="<<p3.age<<", gentder="<<p3.gender<<", tellp "<<writeFile.tellp()<<endl;
    
    writeFile.close();
    cout<<"succ to open file to write : "<<filePath<<endl;
}

void binFileReadPerson(fstream& readFile, PersonData& pObj) {
    // name长度 + name字符串内容
    int strLen = 0;
    readFile.read((char*)(&strLen), sizeof(int));
    readFile>>setw(strLen)>>pObj.name;
    
    // addr长度 + addr字符串内容
    readFile.read((char*)(&strLen), sizeof(int));
    readFile>>setw(strLen)>>pObj.addr;
    
    // age 二进制文件
    readFile.read((char*)(&(pObj.age)), sizeof(pObj.age));
    
    // gender 二进制文件
    readFile.read((char*)(&(pObj.gender)), sizeof(pObj.gender));
}

void binFileRead(void) {
    cout<<endl<<endl<<"binFileRead"<<endl;
    
    // 获取到可执行程序的绝对路径
    char cmdPath[1024] = {0};
    getcwd(cmdPath, 1024);
    
    string filePath = string(cmdPath) + string("/Test.bin");
    fstream readFile(filePath, ios::binary|ios::in); //以二进制文件模式打开Test.bin 准备读取
    if (!readFile) {
        cout<<"fail to open file to read : "<<filePath<<endl;
        return;
    }
    
    int pnum = 0;
    readFile.read((char*)(&pnum), sizeof(int));
    cout<<"after read pnum="<<pnum<<", tellp "<<readFile.tellp()<<endl;
    for (int i=0; i< pnum; ++i) {
        PersonData pobj;
        binFileReadPerson(readFile, pobj);
        cout<<"after read PersonData, name="<<pobj.name<<", addr="<<pobj.addr<<", age="<<pobj.age<<", gentder="<<pobj.gender<<", tellp "<<readFile.tellp()<<endl;
    }
    
    readFile.close();
    cout<<"succ to open file to read : "<<filePath<<endl;
}
